# -*- coding:utf-8 -*-

from cn.pigersing.PyShare.utils.Xml2Obj import Element, Xml2Obj


def expose(f):
    "Decorator to set exposed flag on a function."
    
    f.expose = True
    return f


def is_expose(f):
    "Test whether another function should be publicly exposed."
    
    return getattr(f, 'expose', False) and callable(f)


class ServiceNode(Element):
    
    def __init__(self, service_name, path='.', parent=None, root=None):
        
        super(ServiceNode, self).__init__('Service')
        self.service_name = service_name
        self.path = path
        self.parent = parent
        self.root = root
        self.disabled = False
        
    
    def disabled(): #@NoSelf
        doc = """Set current service disabled""" #@UnusedVariable
       
        def fget(self):
            val = self.getAttribute('disabled')
            return val.lower() == 'true'
           
        def fset(self, value):
            if isinstance(value, bool) :
                value = str(value).lower()
                
            self.setAttribute('disabled', value)
            
            if value == 'true' and self.parent and not hasattr(self.parent, self.service_name):
                    setattr( self.parent, self.service_name, self )
            elif self.parent and  hasattr(self.parent, self.service_name):
                    delattr(self.parent, self.service_name)
            # also set children the value
            for el in self.getElements('Service') :
                el.disabled = value
            
        return locals()
       
    disabled = property(**disabled())
    
    def path(): #@NoSelf
        doc = """path of current service node""" #@UnusedVariable
        def fget(self):
            return self.getAttribute('path')
           
        def fset(self, value):
            if not value.startswith('.') :
                value = '.' + value
            if value.endswith('.') and len(value) > 1 :
                value = value[: -1]
            self.setAttribute('path', value)
           
        return locals()
    path = property(**path())
    
    def service_name(): #@NoSelf
        doc = """Docstring""" #@UnusedVariable
       
        def fget(self):
            return self.getAttribute('service_name')
           
        def fset(self, value):
            self.setAttribute('service_name', value)
           
        return locals()
       
    service_name = property(**service_name())
    
    def registered_method(self):
        # if this node is disabled just return a empty list
        if self.disabled:
            return {}
        
        # filter the method exposed to remote
        meth_dict = {}
        for meth in dir(self) :
            if is_expose(getattr(self, meth)) :
                meth_name = '%s.%s' % (self.path[1:], meth) if len(self.path) > 1 else meth
                meth_dict[meth_name] = (getattr(self, meth), self,)
        
        if self.hasElement():
            for el in self.getElements('Service'):
                meth_dict.update(el.registered_method())
            
        return meth_dict

    def adjust_path(self):
        if self.parent :
            p_path = self.parent.path
            if len(p_path) > 1 :
                self.path = '%s.%s' % (p_path, self.service_name)
            else:
                self.path = self.service_name
            
            for el in self.getElements('Service') :
                el.adjust_path()
        
    def AddChild(self, element):
        if issubclass(element.__class__, ServiceNode) :
            if not getattr(self, element.service_name, False) :          
                element.parent = self
                element.root = self.root
                if self.disabled :
                    element.disabled = True
                else:
                    setattr(self, element.service_name, element)
                # adjust children's path
                element.adjust_path()
            else:
                raise Exception('duplicated node %s' % element.service_name)
        
        return  super(ServiceNode, self).AddChild(element)
    
    @staticmethod
    def from_element(el):        
        return ServiceNode(el.getAttribute('service_name'))
          

class ServiceRoot(ServiceNode):
    
    def __init__(self, server):
        super(ServiceRoot, self).__init__('root', root=self)
        self.server = server
        self.meth_dic = {}
        
    def _dispatch(self, method, params):
        
        if self.meth_dic.has_key(method):
            meth = self.meth_dic[method]
            if not meth[1].disabled :
                if isinstance(params, dict):
                    return meth[0](**params)
                else:
                    return meth[0](*params)
            else :
                raise Exception('method "%s" is disabled and not supported' % method)
    
    def AddChild(self, element):
        super(ServiceRoot, self).AddChild(element)
        # update method dictionary
        self.meth_dic.update(element.registered_method())
        

class ServiceWithEvent(ServiceNode):
    
    def __init__(self, *args, **dic_args):
        super(ServiceWithEvent, self).__init__(*args, **dic_args)
        self._dispatcher = self._default_dispacther
    
    def _default_dispacther(self, *args, **dic_args):
        # simply display it to logger.debug
        logger.debug('[raw parameters]:%s, %s' % (str(args), str(dic_args)))
        
    def set_dispatcher(self, dispatcher):
        self._dispatcher = dispatcher
        
    def dispatch(self, *args, **dic_args):
        self._dispatcher(*args, **dic_args)
        
    
if __name__ == '__main__' :
    
    class S1(ServiceNode):
        
        def __init__(self):
            super(S1, self).__init__('dotted_name')
        def t1(self): pass
        def t2(self): pass
        def _t3(self): pass
        @expose
        def t4(self): pass
    
    s1 = S1()
    s2 = ServiceNode('child')
    s1.AddChild(s2)
    
    print s2
    print s1
    print s1.registered_method()
    
    s3 = S1()
    s4 = ServiceNode('child3')
    s2.AddChild(s3)
    s2.AddChild(s4)
    
    root = ServiceRoot(None)
    root.AddChild(s1)
#    s2.disabled = True
    
    print root.meth_dic
    print root